/* Copyright(C) 2006-2008 Dave Sexton  *
 * http://www.codeplex.com/aip         *
 * http://www.codeplex.com/aip/license *
 ***************************************/
using System;
using System.Collections.Generic;
using System.Text;

namespace DaveSexton.Web.Controls
{
	/// <summary>
	/// An immutable structure that represents a logical <see cref="Position"/> and <see cref="Width"/> on a line.
	/// </summary>
	/// <seealso cref="RandomIntervals"/>
	/// <seealso cref="TranslucentBarsOverlayAutoInputProtectionFilterProvider"/>
	public struct Interval : IEquatable<Interval>, IComparable<Interval>
	{
		#region Public Properties
		/// <summary>
		/// The empty interval, which has a <see cref="Position"/> and <see cref="Width"/> of zero.
		/// </summary>
		public static readonly Interval Empty = new Interval();

		/// <summary>
		/// Gets the location of the <see cref="Interval"/> on a line.
		/// </summary>
		/// <value>The location of the <see cref="Interval"/> on a line.</value>
		public int Position
		{
			get
			{
				return position;
			}
		}

		/// <summary>
		/// Gets the size of the <see cref="Interval"/>.
		/// </summary>
		/// <value>The size of the <see cref="Interval"/>.</value>
		public int Width
		{
			get
			{
				return width;
			}
		}
		#endregion

		#region Private / Protected
		private readonly int position, width;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="Interval" /> structure with the specified
		/// <paramref name="position"/> and <paramref name="width"/>.
		/// </summary>
		/// <param name="position">The location of the <see cref="Interval"/> on a line.</param>
		/// <param name="width">The size of the <see cref="Interval"/>.</param>
		public Interval(int position, int width)
		{
			this.position = position;
			this.width = width;
		}
		#endregion

		#region Methods
		/// <summary>
		/// Determines whether two <see cref="Interval">Intervals</see> intersect on the same line.
		/// </summary>
		/// <seealso cref="Overlaps(Interval,Interval)"/>
		/// <param name="interval1">The first <see cref="Interval"/>.</param>
		/// <param name="interval2">The second <see cref="Interval"/>.</param>
		/// <returns><see langword="true">True</see> if one of the specified <see cref="Interval">Intervals</see> 
		/// <see cref="Overlaps(Interval,Interval)"/> the other; otherwise, <see langword="false"/>.</returns>
		public static bool IntersectsWith(Interval interval1, Interval interval2)
		{
			return Overlaps(interval1, interval2) || Overlaps(interval2, interval1);
		}

		/// <summary>
		/// Determines whether <paramref name="interval1" /> starts after <paramref name="interval2" /> begins, and also before it ends.
		/// </summary>
		/// <remarks>
		/// This method only checks that <paramref name="interval1"/> starts after, and overlaps, <paramref name="interval2"/>.
		/// It does not return <see langword="true"/> when the <see cref="Width"/> of <paramref name="interval1"/> extends past the <see cref="Position"/>
		/// of <paramref name="interval2"/>.  However, <see cref="IntersectsWith(Interval,Interval)"/> will return <see langword="true"/> in that case since 
		/// it checks for bidirectional overlapping.
		/// </remarks>
		/// <seealso cref="IntersectsWith(Interval,Interval)"/>
		/// <param name="interval1">The first <see cref="Interval"/>.</param>
		/// <param name="interval2">The second <see cref="Interval"/>.</param>
		/// <returns><see langword="true">True</see> if <paramref name="interval1" /> starts after <paramref name="interval2" /> begins, and also before it ends; 
		/// otherwise, <see langword="false"/>.</returns>
		public static bool Overlaps(Interval interval1, Interval interval2)
		{
			return interval1.position > interval2.position && interval1.position < interval2.position + interval2.width;
		}

		/// <summary>
		/// Determines whether this <see cref="Interval"/> intersects the specified <paramref name="interval"/>.
		/// </summary>
		/// <seealso cref="IntersectsWith(Interval,Interval)"/>
		/// <param name="interval">The <see cref="Interval"/> to test.</param>
		/// <returns><see langword="true">True</see> if this <see cref="Interval"/> <see cref="IntersectsWith(Interval,Interval)">intersects</see> 
		/// the other; otherwise, <see langword="false"/>.</returns>
		public bool IntersectsWith(Interval interval)
		{
			return IntersectsWith(this, interval);
		}

		/// <summary>
		/// Determines whether this <see cref="Interval"/> starts after the specified <paramref name="interval" /> begins, and also before it ends.
		/// </summary>
		/// <remarks>
		/// This method only checks that <paramref name="interval1"/> starts after, and overlaps, <paramref name="interval2"/>.
		/// It does not return <see langword="true"/> when the <see cref="Width"/> of <paramref name="interval1"/> extends past the <see cref="Position"/>
		/// of <paramref name="interval2"/>.  However, <see cref="IntersectsWith(Interval,Interval)"/> will return <see langword="true"/> in that case since 
		/// it checks for bidirectional overlapping.
		/// </remarks>
		/// <seealso cref="Overlaps(Interval,Interval)"/>
		/// <param name="interval">The <see cref="Interval"/> to test.</param>
		/// <returns><see langword="true">True</see> if this <see cref="Interval"/> starts after the specified <paramref name="interval" /> begins, and also 
		/// before it ends; otherwise, <see langword="false"/>.</returns>
		public bool Overlaps(Interval interval)
		{
			return Overlaps(this, interval);
		}

		/// <summary>
		/// Determines whether the specified <see cref="Interval">Intervals</see> are equal.
		/// </summary>
		/// <param name="i1">The first <see cref="Interval"/>.</param>
		/// <param name="i2">The second <see cref="Interval"/>.</param>
		/// <returns><see langword="true">True</see> if the specified <see cref="Interval">Intervals</see> are equal; otherwise, 
		/// <see langword="false"/>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "i")]
		public static bool operator ==(Interval i1, Interval i2)
		{
			return i1.Equals(i2);
		}

		/// <summary>
		/// Determines whether the specified <see cref="Interval">Intervals</see> are not equal.
		/// </summary>
		/// <param name="i1">The first <see cref="Interval"/>.</param>
		/// <param name="i2">The second <see cref="Interval"/>.</param>
		/// <returns><see langword="true">True</see> if the specified <see cref="Interval">Intervals</see> are not equal; otherwise, 
		/// <see langword="false"/>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "i")]
		public static bool operator !=(Interval i1, Interval i2)
		{
			return !i1.Equals(i2);
		}

		/// <summary>
		/// Determines whether the <see cref="Position"/> of <paramref name="i1"/> is greater than the Position of <paramref name="i2"/>.
		/// </summary>
		/// <param name="i1">The first <see cref="Interval"/>.</param>
		/// <param name="i2">The second <see cref="Interval"/>.</param>
		/// <returns><see langword="true">True</see> if the <see cref="Position"/> of <paramref name="i1"/> is greater than the Position of
		/// <paramref name="i2"/>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "i")]
		public static bool operator >(Interval i1, Interval i2)
		{
			return i1.position > i2.position;
		}

		/// <summary>
		/// Determines whether the <see cref="Position"/> of <paramref name="i1"/> is less than the Position of <paramref name="i2"/>.
		/// </summary>
		/// <param name="i1">The first <see cref="Interval"/>.</param>
		/// <param name="i2">The second <see cref="Interval"/>.</param>
		/// <returns><see langword="true">True</see> if the <see cref="Position"/> of <paramref name="i1"/> is less than the Position of
		/// <paramref name="i2"/>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "i")]
		public static bool operator <(Interval i1, Interval i2)
		{
			return i1.position < i2.position;
		}

		/// <summary>
		/// Determines whether this <see cref="Interval"/> is equal to the object specified.
		/// </summary>
		/// <param name="obj">The <see cref="object"/> to be compared.</param>
		/// <returns><see langword="true">True</see> if this <see cref="Interval"/> is equal to the object specified.</returns>
		public override bool Equals(object obj)
		{
			if (obj is Interval)
				return Equals((Interval) obj);
			else
				return false;
		}

		/// <summary>
		/// Returns the hash code for this instance.
		/// </summary>
		/// <returns>The hash code for this instance.</returns>
		public override int GetHashCode()
		{
			return position ^ width;
		}

		/// <summary>
		/// Returns a string representation of the <see cref="Interval"/>.
		/// </summary>
		/// <returns>String representation of the <see cref="Interval"/>.</returns>
		public override string ToString()
		{
			return "{" + position + "," + width + "}";
		}
		#endregion

		#region IEquatable<Interval> Members
		/// <summary>
		/// Determines whether this <see cref="Interval"/> is equal to the specified Interval.
		/// </summary>
		/// <param name="other">The <see cref="Interval"/> to be compared.</param>
		/// <returns><see langword="true">True</see> if this <see cref="Interval"/> is equal to the specified Interval.</returns>
		public bool Equals(Interval other)
		{
			return other.position == position && other.width == width;
		}
		#endregion

		#region IComparable<Interval> Members
		/// <summary>
		/// Compares the <see cref="Position"/> of this <see cref="Interval"/> to the Position of the specified Interval.
		/// </summary>
		/// <param name="other">The <see cref="Interval"/> to be compared.</param>
		/// <returns>A signed number indicating the relative values of this instance and 
		/// <paramref name="other"/>: Less than zero means that this instance is less than 
		/// <paramref name="other"/>; zero means that this instance is equal to <paramref name="other"/>;
		/// greater than zero means that this instance is greater than <paramref name="other"/>.</returns>
		public int CompareTo(Interval other)
		{
			return position.CompareTo(other.position);
		}
		#endregion
	}
}
